<?php

namespace Hilaoyu\Utils\Drivers\Sms;


use Carbon\Carbon;
use Hilaoyu\Utils\Traits\ErrorsTrait;
use Hilaoyu\Utils\UtilArr;
use Hilaoyu\Utils\UtilRegex;
use Hilaoyu\Utils\UtilStr;
use stdClass;

/**
 * Class YiMeiRuanTong
 * @package Hilaoyu\Utils\Drivers\Sms
 * 官网：https://www.emay.cn/
 * 接口文档：https://www.b2m.cn/static/doc/sms/onlysms_or.html
 *
 */
class YiMeiRuanTong
{
    use ErrorsTrait;

    /**
     * @var array
     */
    protected $config = [];
    /**
     * @var string
     */
    private string $iv = '';
    /**
     * @var string|array|mixed
     */
    private string $encryptKey;
    /**
     * @var string
     */
    private string $chipher = 'AES-128-ECB';
    /**
     * @var string
     */
    private string $single_url;
    /**
     * @var string
     */
    private string $batch_url;
    /**
     * @var bool
     */
    private bool $sms_gzip = true;

    /**
     * @param $config
     */
    public function __construct($config = [])
    {
        $this->setConfig($config);
        $this->encryptKey = (string)data_get($this->config, 'app_secret');
        $basic_url = (string)data_get($this->config, 'sms_basic_url','http://www.btom.cn:8080');
        if (!$basic_url){
            throw new \Exception('请配置短信地址！');
        }
        $this->single_url = UtilStr::finish($basic_url, '/').'inter/sendSingleSMS';
        $this->batch_url  = UtilStr::finish($basic_url, '/').'inter/sendBatchOnlySMS';
        if (isset($config['sms_gzip'])) {
            $this->sms_gzip = (bool)data_get($config, 'sms_gzip');
        }
        if (!$this->encryptKey) {
            throw new \Exception('请配置短信密钥！');
        }
    }

    /**
     *
     *
     * @param $config
     * @return $this
     * @author Iwtrgw 2025/8/14 09:12
     */
    public function setConfig($config = [])
    {
        $this->config = (array)$config;
        return $this;
    }

    /**
     *
     *
     * @param $key
     * @return array|\ArrayAccess|mixed
     * @author Iwtrgw 2025/4/11 09:28
     */
    public function getConfig($key = '')
    {
        $key = trim($key);

        if ($key) {
            return UtilArr::get($this->config, $key);
        }

        return $this->config;
    }

    /**
     *
     *
     * @param $content
     * @param $tpl
     * @return array|mixed|string|string[]|null
     * @author Iwtrgw 2025/4/11 09:28
     */
    protected function buildTplValue($content, $tpl = '')
    {
        if (empty($tpl)) {
            return UtilArr::toString($content);
        }

        // $content = UtilStr::replace($tpl, (array)$content);
        $content = $content ? (array)$content : [];
        $content = UtilStr::replace($tpl, $content);

        return $content;
    }


    /**
     * 发送短信(单条)
     *
     * @param string $phone
     * @param string|array $content
     * @param string|null $tpl
     * @param int $validPeriodtime
     * @param string $timerTime
     * @param string $customSmsId
     * @param string $extendedCode
     * @return false|stdClass
     * @author Iwtrgw 2025/4/11 11:25
     */
    public function send(string $phone, $content, ?string $tpl = '', int $validPeriodtime = 120, string $timerTime = "", string $customSmsId = "",
                         string $extendedCode = "")
    {
        $phone = trim($phone);
        $tpl   = trim($tpl);
        if (!UtilRegex::isMobile($phone)) {
            $this->setError('手机号格式错误');
            return false;
        }
        $item          = new stdClass();
        $item->mobile  = $phone;
        if (!$tpl){
            $tpl = (string)$this->getConfig('tpl.captcha');
        }
        if (!$tpl){
            $this->setError('请配置短信签名');
            return false;
        }
        $item->content = $this->buildTplValue($content, $tpl);
        $jsonData      = $this->buildRequestData($item, $validPeriodtime, $timerTime, $customSmsId, $extendedCode);
        $sendData      = $this->encrypt($jsonData);
        if (!$sendData) {
            $this->setError('数据加密构造失败');
            return false;
        }
        try {
            $res = $this->httpRequest($this->single_url, $sendData);
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $result = strtoupper((string)data_get($res, 'result'));
        if ('SUCCESS' !== $result) {
            $this->setError('发送失败:' . $result);
            return false;
        }
        $res->plaintext = $this->decrypt($res->ciphertext);
        return $res;
    }

    /**
     * 批量发送短信（一次最多500个手机)
     *
     * @param array $phones
     * @param string|array $content
     * @param string $tpl
     * @param int $validPeriodtime 请求有效时间(必填)[单位:秒]
     * @param string $timerTime 定时发送时间(选填，定时时间在90天之内),格式：yyyy-MM-dd HH:mm:ss,如果不填，则为即时发送
     * @param string $customSmsId 自定义消息ID(选填),最长32位
     * @param string $extendedCode 扩展码(选填)最长支持12位，如果最终号码长度超长，会截取扩展码
     * @return false|void
     * @author Iwtrgw 2025/4/11 10:25
     */
    public function sendBatch(array  $phones,  $content, ?string $tpl = '', int $validPeriodtime = 120, string $timerTime = "", string $customSmsId = "",
                              string $extendedCode = "")
    {
        $tpl = trim($tpl);
        if (!$tpl){
            $tpl = (string)$this->getConfig('tpl.captcha');
        }
        if (!$tpl){
            $this->setError('请配置短信签名');
            return false;
        }
        $smses = array();
        // 验证失败数组
        $failPhones = array();
        foreach ($phones as $phone) {
            if (!UtilRegex::isMobile($phone)) {

                $failPhones[] = $phone;
            }
            $smses[] = $phone;
        }
        if (!empty($failPhones)){
            $this->setError('以下手机号格式错误，无法发送短信:' . implode(',', $failPhones));
        }
        $item          = new stdClass();
        $item->mobiles   = $smses;
        $item->content = $this->buildTplValue($content, $tpl);
        $jsonData      = $this->buildRequestData($item, $validPeriodtime, $timerTime, $customSmsId, $extendedCode);
        $sendData      = $this->encrypt($jsonData);
        if (!$sendData) {
            $this->setError('数据加密构造失败');
            return false;
        }
        try {
            $res = $this->httpRequest($this->batch_url, $sendData);
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
            return false;
        }
        $res->plaintext = $this->decrypt($res->ciphertext);
        return $res;
    }

    /**
     *
     *
     * @param Object $stdClass
     * @param int $validPeriodtime
     * @param string $timerTime
     * @param string $customSmsId
     * @param string $extendedCode
     * @return void
     * @author Iwtrgw 2025/4/11 11:05
     */
    private function buildRequestData(object $stdClass, int $validPeriodtime = 120, string $timerTime = "", string $customSmsId = "",
                                      string $extendedCode = "")
    {
        /* 选填内容 */
        if ("" != $timerTime) {
            $stdClass->timerTime = $timerTime;
        }
        if ("" != $customSmsId) {
            $stdClass->customSmsId = $customSmsId;
        }
        if ("" != $extendedCode) {
            $stdClass->extendedCode = $extendedCode;
        }
        $stdClass->requestTime        = Carbon::now()->getPreciseTimestamp(3);
        $stdClass->requestValidPeriod = $validPeriodtime;
        return json_encode($stdClass, JSON_UNESCAPED_UNICODE);
    }

    /**
     *
     *
     * @param string $url
     * @param string $data
     * @return stdClass|null
     * @author Iwtrgw 2025/4/11 14:27
     */
    public function httpRequest(string $url, string $data)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_HEADER, true);
        $app_id = (string)data_get($this->config, 'app_key');
        if (!$url) {
            throw new \Exception('未配置发送地址');
        }
        if (!$app_id) {
            throw new \Exception('未配置app_id');
        }
        $header[] = "appId: " . $app_id;
        if ($this->sms_gzip) {
            $header[] = "gzip: on";
        }
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

        curl_setopt($curl, CURLOPT_HEADER, true);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
        $res = curl_exec($curl);
        // 获取curl错误
        if (curl_errno($curl)) {
            throw new \Exception('短信发送失败: curl');
        }
        $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
        curl_close($curl);

        $header = substr($res, 0, $headerSize);

        $outobj = new stdClass();

        $lines = explode("\r\n", $header);
        foreach ($lines as $line) {
            $items = explode(": ", $line);
            if (data_get($items, 0) && data_get($items, 1)) {
                data_set($outobj, $items[0], $items[1]);
            }
        }

        $outobj->ciphertext = substr($res, $headerSize);

        return $outobj;

    }

    /**
     * 数据加密
     *
     * @param string $encryptStr
     * @return mixed|string
     * @author Iwtrgw 2025/4/11 10:38
     */
    private function encrypt(string $encryptStr)
    {
        if ($this->sms_gzip) {
            $encryptStr = gzencode($encryptStr);
        }
        if (version_compare(PHP_VERSION, '7.0.0', 'ge')) { //php7.0以上
            return openssl_encrypt($encryptStr, $this->chipher, $this->encryptKey, OPENSSL_RAW_DATA, $this->iv);
        }
        $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, $this->iv);

        mcrypt_generic_init($module, $this->encryptKey, $this->iv);

        $block      = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_ECB);
        $pad        = $block - (strlen($encryptStr) % $block);
        $encryptStr .= str_repeat(chr($pad), $pad);

        $encrypted = mcrypt_generic($module, $encryptStr);

        mcrypt_generic_deinit($module);
        mcrypt_module_close($module);

        return $encrypted;
    }


    /**
     * 数据解密
     *
     * @param string $decryptStr
     * @return false|mixed|string
     * @author Iwtrgw 2025/4/11 10:43
     */
    private function decrypt(string $decryptStr)
    {
        if (version_compare(PHP_VERSION, '7.0.0', 'ge')) { // php7.0以上
            $decryptedData = openssl_decrypt($decryptStr, $this->chipher, $this->encryptKey, OPENSSL_RAW_DATA, $this->iv);
        } else {
            $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, $this->iv);

            mcrypt_generic_init($module, $this->encryptKey, $this->iv);

            $decryptedData = mdecrypt_generic($module, $decryptStr);
        }
        if ($this->sms_gzip && $decryptedData) {
            $decryptedData = gzdecode($decryptedData);
        }

        return $decryptedData;
    }
}
